//快读
#ifndef MKAHYPAR_FAST_IO_H
#define MKAHYPAR_FAST_IO_H

#include <cstdint>
#include <cstdio>
#include <iostream>
#include <string>
#include "mkahypar/utils/typedef.h"
#include "mkahypar/partition/context.h"
#include "mkahypar/utils/macros.h"
#include "mkahypar/datastructure/fpga_graph.h"


namespace mkahypar {
  namespace fastIO {
    class FastIO {
    public:
      FastIO(const std::string &filename) {
        file = fopen(filename.c_str(), "r");
        ch = EOF;
        p1 = in;
        p2 = in;
      }

      ~FastIO() {
        fclose(file);
      }

      template<typename type>
      inline void readNum(type &x) {
        x = 0;
        bool flag(0);
        ch = getchar();
        while (!isdigit(ch)) flag ^= ch == '-', ch = getchar();
        while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
        flag ? x = -x : 0;
      }

      inline void readChar(std::string &x) {
        do {
          ch = getchar();
        }while(ch == ' ');
        while (isalpha(ch) || isdigit(ch)) {
          x += ch;
          ch = getchar();
        }
      }

      bool isVaild() {
        return isdigit(ch) || isalpha(ch);
      }

      int getReadState() {
        if (ch == EOF)
          return 0;
        if (ch == ' ')
          return 1;
        return 2;
      }


      inline char getchar() {
        return p1 == p2 && (p2 = (p1 = in) + fread(in, 1, SIZE, file), p1 == p2) ? EOF : *p1++;
      }

      FILE *file;
      char *p1, *p2, ch;
      char in[1 << 20];//输入缓冲区
      int32_t SIZE = 1 << 20;
    };


    //读取节点定义文件
    static void inline
    readDesignAre(const std::string &design_are, Hypergraph &hypergraph) {
      FastIO file(design_are);
      HypernodeID num_hypernodes = 0;
      do {
        std::string name;
        ds::NodeResource weight;
        file.readChar(name);
        //防止最后一行有多的换行
        if (name.size() == 0)
          break;
        //建立映射
        Context::getInstance().nodeName2id[name] = num_hypernodes;
        Context::getInstance().id2nodeName[num_hypernodes] = name;
//        LOG << name << "to" << num_hypernodes;
        ++num_hypernodes;

//        LLOG<<name;
        for (int i = 0; i < 8; ++i) {
          file.readNum(weight[i]);
//          LLOG<<weight[i];
        }
//        LOG<<"";
        hypergraph.createHyperNode(weight);
      } while (file.getReadState());

      LOG << "num_hypernodes:" << num_hypernodes;

    }

    //读取net文件
//    static void inline
//    readDesignNet(const std::string &design_net, Hypergraph &hypergraph, HypernodeID &pinIndex) {
//      FastIO file(design_net);
//      std::vector<std::string> target;
//      std::vector<HypernodeID> edge_vector;//存所有的
//
//      HyperedgeID num_hyperedges = 0;
//      do {
//        std::string source;
//        target.clear();
//        HyperedgeWeight weight;
//        file.readChar(source);
//        //防止最后空行
//        if (source.size() == 0)
//          break;
//
//
//
//
//        if(Context::getInstance().nodeName2id.count(source) == false){
//          LOG<<source;
//          ERROREXIT("error!");
//        }
//
//        file.readNum(weight);
//        //创建超边
//        HypernodeID source_id = Context::getInstance().nodeName2id[source];
//        hypergraph.createHyperEdge();
//        hypergraph.hyperedge(num_hyperedges).setFirstEntry(pinIndex);
//        hypergraph.hyperedge(num_hyperedges).setWeight(weight);
//        hypergraph.hyperedge(num_hyperedges).setSource(source_id);
//        hypergraph.hyperedge(num_hyperedges).incrementSize();
//        hypergraph.hyperedge(num_hyperedges).hash += math::hash(source_id);
//        hypergraph._incidence_array.push_back(source_id);
//        hypergraph.hypernode(source_id).incidentNets().emplace_back(num_hyperedges);
//        ++pinIndex;
//        std::cout<<source<<" "<<weight<<" ";
//        for (;;) {
//          std::string target;
//          file.readChar(target);
////          LLOG<<target;
//          if(target.size() == 0)
//            break;
//          std::cout<<target<<" ";
//          if(Context::getInstance().nodeName2id.count(target) == false){
//            LOG<<target;
//            ERROREXIT("error!");
//          }
//          HypernodeID target_id = Context::getInstance().nodeName2id[target];
//          hypergraph.hyperedge(num_hyperedges).incrementSize();
//          hypergraph.hyperedge(num_hyperedges).hash += math::hash(target_id);
//
//          hypergraph._incidence_array.push_back(target_id);
//          hypergraph.hypernode(target_id).incidentNets().emplace_back(num_hyperedges);
//          ++pinIndex;
//
//          if (file.getReadState() == 2 || file.getReadState() == 0)//读的是换行或者eof
//            break;
//        }
//        std::cout<<std::endl;
//
//        ++num_hyperedges;
////        hypergraph.hyperedge(num_hyperedges - 1).firstEntry();
////        hypergraph.hyperedge(num_hyperedges - 1).firstInvalidEntry();
////         DBG<< "hyper edge:" << num_hyperedges - 1 << hypergraph.hyperedge(num_hyperedges - 1).firstEntry() << hypergraph.hyperedge(num_hyperedges - 1).firstInvalidEntry();
//      } while (file.getReadState());
//
//      //添加一个哨兵
//      hypergraph._hyperedges.emplace_back(hypergraph.hyperedge(num_hyperedges - 1).firstInvalidEntry(), 0, 0);
//
//
//
//      LOG << "num_hyperedges:" << num_hyperedges;
//      LOG << "total pin num:" << pinIndex;
//
//    }

    //基于流的读
    static bool getNextLine(std::ifstream &file, std::string &line, size_t &line_number) {
      bool success = false;
      do {
        success = static_cast<bool>(std::getline(file, line));
        ++line_number;
        // skip any comments
      } while (success && line[0] == '%');
      return success;
    }

    static void inline
    readDesignNet(const std::string &design_net, Hypergraph &hypergraph, HypernodeID &pinIndex) {
      std::ifstream file(design_net);
      std::vector<std::string> target;
      std::vector<HypernodeID> edge_vector;//存所有的

      HyperedgeID num_hyperedges = 0;
      std::string line;
      size_t line_number = -1;
      while (getNextLine(file, line, line_number)){
        std::stringstream ss(line);
        std::string source;
        ss>>source;
        if(Context::getInstance().nodeName2id.count(source) == false){
          LOG<<source;
          ERROREXIT("error!");
        }

        HyperedgeWeight weight;
        ss >> weight;
        //创建超边
        HypernodeID source_id = Context::getInstance().nodeName2id[source];
        hypergraph.createHyperEdge();
        hypergraph.hyperedge(num_hyperedges).setFirstEntry(pinIndex);
        hypergraph.hyperedge(num_hyperedges).setWeight(weight);
        hypergraph.hyperedge(num_hyperedges).setSource(source_id);
        hypergraph.hyperedge(num_hyperedges).incrementSize();
        hypergraph.hyperedge(num_hyperedges).hash += math::hash(source_id);
        hypergraph._incidence_array.push_back(source_id);
        hypergraph.hypernode(source_id).incidentNets().emplace_back(num_hyperedges);
        ++pinIndex;
        std::cout<<source<<" "<<weight<<" ";
        std::string target;
        while(ss >> target){
          if(Context::getInstance().nodeName2id.count(target) == false){
            LOG<<target;
            ERROREXIT("error!");
          }
          std::cout<<target<<" ";
          HypernodeID target_id = Context::getInstance().nodeName2id[target];
          hypergraph.hyperedge(num_hyperedges).incrementSize();
          hypergraph.hyperedge(num_hyperedges).hash += math::hash(target_id);

          hypergraph._incidence_array.push_back(target_id);
          hypergraph.hypernode(target_id).incidentNets().emplace_back(num_hyperedges);
          ++pinIndex;
        }
        std::cout<<std::endl;
        ++num_hyperedges;
      }
      //添加一个哨兵
      hypergraph._hyperedges.emplace_back(hypergraph.hyperedge(num_hyperedges - 1).firstInvalidEntry(), 0, 0);


      LOG<<"total line num"<<line_number;
      LOG << "num_hyperedges:" << num_hyperedges;
      LOG << "total pin num:" << pinIndex;

    }
    //读取info文件
    static void inline
    readDesignInfo(const std::string &design_info, TargetGraph &topo, int &fpga_num) {
      FastIO file(design_info);
      HyperedgeWeight bound;

      fpga_num = 0;
      do {
        std::string name;
        NodeWeight weight(8);
        file.readChar(name);
        //防止最后一行有多的换行
        if (name.size() == 0)
          break;
        //映射
        Context::getInstance().fpgaName2id[name] = fpga_num;
        Context::getInstance().id2fpgaName[fpga_num] = name;
        ++fpga_num;
        file.readNum(bound);
        LLOG << name << bound;
        for (int i = 0; i < 8; ++i) {
          file.readNum(weight[i]);
          LLOG << weight[i];
        }
        LOG << "";

        //创建节点
        topo.createNode(bound, weight);
      } while (file.getReadState());
      LOG << "fpga num:" << fpga_num;
    }

    //读取topo文件
    static void inline
    readDesignTopo(const std::string &design_info, TargetGraph &topo) {
      FastIO file(design_info);
      HyperedgeWeight bound;
      file.readNum(bound);
      LOG << "hop limit:" << bound;
      topo.hop_limit = bound;//设置跳数上界
      Context::getInstance().hop_limit = bound;
      do {
        std::string fpga1, fpga2;
        file.readChar(fpga1);
        //最后一行换行
        if (fpga1.size() == 0)
          break;
        file.readChar(fpga2);
        //映射
        if(!Context::getInstance().fpgaName2id.count(fpga1) ||
                !Context::getInstance().fpgaName2id.count(fpga2))
          ERROREXIT("error!");
        int u = Context::getInstance().fpgaName2id[fpga1];
        int v = Context::getInstance().fpgaName2id[fpga2];
        //创建边
        topo.createEdge(u, v);
        LOG << fpga1 << fpga2;
      } while (file.getReadState());

    }

    //创建超图
    static inline void createHypergraphFromFile(const std::string &design_are,
                                                const std::string &design_net,
                                                const std::string &design_info,
                                                const std::string &design_topo,
                                                Hypergraph &hypergraph,
                                                TargetGraph &topo) {

      int part_num;
      HypernodeID pin_index = 0;
      readDesignAre(design_are, hypergraph);
      readDesignNet(design_net, hypergraph, pin_index);
      readDesignInfo(design_info, topo, part_num);
      readDesignTopo(design_topo, topo);

      //超图初始话
      hypergraph.initialize(part_num, pin_index);
      //topo初始话
      topo.initialize();

    }

    //打印划分结果
    void printPartitionResult(Hypergraph& hg,TargetGraph& topo){
      std::ofstream file(Context::getInstance().outputPath);
      int num_partition = hg._k;
      std::vector<std::vector<HypernodeID>>result(num_partition);
      for(const HypernodeID& hn : hg.nodes()){
        result[hg.partID(hn)].emplace_back(hn);
      }
      for(PartitionID id = 0; id < num_partition;++id){
        file<<Context::getInstance().id2fpgaName[id]<<": ";
        for(int i=0;i<result[id].size();++i){
          if(i==0)
            file<<Context::getInstance().id2nodeName[result[id][i]];
          else
            file<<" "<<Context::getInstance().id2nodeName[result[id][i]];

        }
        file<<std::endl;
      }
      file.close();
    }

    //还原design.net
    void restoreDesignNet(Hypergraph &hg) {
      for (auto i = 0; i < hg._num_hyperedges; ++i) {
        auto &edge = hg.hyperedge(i);
        LLOG << Context::getInstance().id2nodeName[edge.source()] << edge.weight();
        for (auto &v: hg.pinHandles(i)) {
          if (v != edge.source())
            LLOG << Context::getInstance().id2nodeName[v];
        }
        LOG << "";
      }
    }


  }
}
#endif //MKAHYPAR_FAST_IO_H
